package com.bovink.camera;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.media.ExifInterface;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

/**
 * 拍摄Activity
 */
@SuppressWarnings("deprecation")
public class CameraActivity extends AppCompatActivity {

    /**
     * 摄像预览、结果层
     */
    @BindView(R.id.fl_view)
    FrameLayout viewFrameLayout;
    /**
     * 拍摄成功的图片
     */
    @BindView(R.id.iv_picture)
    ImageView pictureView;
    /**
     * 拍摄成功的视频
     */
    @BindView(R.id.tv_video)
    TextureView videoTextureView;
    /**
     * 摄像头预览
     */
    @BindView(R.id.tv_camera)
    TextureView cameraTextureView;
    /**
     * 进度按钮
     */
    @BindView(R.id.circle_progress)
    CircleProgressBar circleProgressBar;
    /**
     * 确认按钮
     */
    @BindView(R.id.iv_confirm)
    ImageView confirmImageView;
    /**
     * 取消按钮
     */
    @BindView(R.id.iv_cancel)
    ImageView cancelImageView;
    /**
     * 提示语
     */
    @BindView(R.id.tv_hint)
    TextView hintTextView;

    /**
     * 摄像头
     */
    private Camera camera;
    /**
     * 摄像头参数
     */
    private Camera.Parameters parameters;
    /**
     * 视频录制
     */
    private MediaRecorder recorder;
    /**
     * 视频播放
     */
    private MediaPlayer mediaPlayer;

    /**
     * 录制的视频文件
     */
    private File resultFile;

    /**
     * 需要申请的权限
     */
    private String[] requestPermissions = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE};
    /**
     * 事件类型
     */
    private int eventType = 0;
    /**
     * 拍摄照片
     */
    public final static int TAKE_PICTURE = 1;
    /**
     * 录制视频
     */
    public final static int RECORD_VIDEO = 2;
    /**
     * 功能类型
     */
    public static int type = -1;
    /**
     * 文件地址
     */
    public static String filePath;
    public static boolean gotoSetting = false;
    /**
     * 视频的宽度
     */
    private int videoWidth = 0;
    /**
     * 视频的高度
     */
    private int videoHeight = 0;
    /**
     * 权限被拒绝
     */
    private boolean permissionDenied = false;
    /**
     * 提示用户需要开启的权限
     */
    private int alertString[] = {R.string.need_camera_permission,
            R.string.need_audio_permission,
            R.string.need_read_permission};
    /**
     * 上下文
     */
    private Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        context = this;

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 全屏，隐藏状态栏
        setContentView(R.layout.activity_camera);
        ButterKnife.bind(this);

        requestPermission(0);

        setDefaultSavePath();

        setListeners();

        if (type == TAKE_PICTURE) {
            hintTextView.setText("轻触拍照");
            circleProgressBar.setLongClickable(false);
        } else if (type == RECORD_VIDEO) {
            hintTextView.setText("长按摄像");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releasePlayer();
        releaseRecorder();
        releaseCamera();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (grantResults[0]) {
            case PackageManager.PERMISSION_GRANTED:
                if (requestCode == requestPermissions.length - 1) {// 获得所有权限

                    cameraTextureView.setVisibility(View.VISIBLE);// 将触发SurfaceTextureListener中的事件
                } else {

                    requestPermission(requestCode + 1);
                }
                break;
            case PackageManager.PERMISSION_DENIED:
                MyDialogFragment dialogFragment =
                        MyDialogFragment.newInstance(getString(alertString[requestCode])
                                + getString(R.string.alert_permission));
                dialogFragment.show(getSupportFragmentManager(), null);
                permissionDenied = true;
                break;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (permissionDenied && gotoSetting) {
            requestPermission(0);
            gotoSetting = false;
            permissionDenied = false;
        }
        if (resultFile != null) {
            if (eventType == RECORD_VIDEO) {

                viewFrameLayout.addView(videoTextureView);
                videoTextureView.setVisibility(View.VISIBLE);
            }
        }
        if (cameraTextureView.getParent() == null) {
            viewFrameLayout.addView(cameraTextureView);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (resultFile != null) {
            if (eventType == RECORD_VIDEO) {

                viewFrameLayout.removeView(videoTextureView);
                videoTextureView.setVisibility(View.GONE);
            }
        }
        if (cameraTextureView.getParent() != null) {
            viewFrameLayout.removeView(cameraTextureView);
        }
    }

    /**
     * 设置默认照片和视频储存路径
     */
    private void setDefaultSavePath() {
        if (filePath == null) {

            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Files/");
            if (!file.exists()) {
                file.mkdirs();
            }
            filePath = file.getAbsolutePath();
        }
    }

    /**
     * 设置事件监听
     */
    private void setListeners() {

        // 点击事件
        circleProgressBar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (type == RECORD_VIDEO) {
                    return;
                }
                if (permissionDenied) {
                    Toast.makeText(context, "没有权限，无法使用功能", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (camera == null) {
                    Toast.makeText(context, "相机无法使用", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 拍照片
                eventType = TAKE_PICTURE;
                takePicture();
            }
        });

        // 长按事件
        circleProgressBar.setOnLongClickListener(new CircleProgressBar.OnLongClickListener() {
            @Override
            public void onLongClick() {
                if (permissionDenied) {
                    Toast.makeText(context, "没有权限，无法使用功能", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (camera == null) {
                    Toast.makeText(context, "相机无法使用", Toast.LENGTH_SHORT).show();
                    return;
                }

                // 录视频
                eventType = RECORD_VIDEO;
                startRecord();
            }
        });

        // 视频录制结束事件
        circleProgressBar.setOnVideoRecordedListener(new CircleProgressBar.OnVideoRecordedListener() {
            @Override
            public void onVideoRecorded() {
                if (permissionDenied || camera == null) {
                    return;
                }

                // 停止录制视频
                releaseRecorder();
                // 隐藏拍摄按钮，显示确认和取消按钮
                showHandleButton(true);

                // 删除cameraTextureView释放摄像头
                if (cameraTextureView.getParent() != null) {
                    viewFrameLayout.removeView(cameraTextureView);
                }
                cameraTextureView.setVisibility(View.GONE);

                if (videoTextureView.getParent() != null) {
                    viewFrameLayout.removeView(videoTextureView);
                }
                viewFrameLayout.addView(videoTextureView, 0);
                videoTextureView.setVisibility(View.VISIBLE); // 触发SurfaceTextureListener
            }
        });

        // 预览视图变化事件
        cameraTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                System.out.println("CameraActivity.onSurfaceTextureAvailable");
                initCamera();
                if (camera != null && eventType == 0) {
                    // 开始预览
                    camera.startPreview();
                }
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                System.out.println("CameraActivity.onSurfaceTextureDestroyed");
                releaseCamera();
                return true;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });

        // 视频视图变化事件
        videoTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                initPlayer(surface);
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                releasePlayer();
                return true;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

    /**
     * 请求权限
     * index是权限在String[]中的序号
     * 配合{@link #onRequestPermissionsResult(int, String[], int[])}逐个申请String[]中包含的权限
     *
     * @param index index是权限在String数组中的序号
     */
    private void requestPermission(int index) {

        for (int i = index; i < requestPermissions.length; i++) {
            if (ActivityCompat.checkSelfPermission(this, requestPermissions[i]) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{requestPermissions[i]}, i);
                break;
            }
            if (i == requestPermissions.length - 1) {// 获得所有权限

                cameraTextureView.setVisibility(View.VISIBLE);// 将触发SurfaceTextureListener中的事件
            }
        }
    }

    /**
     * 旋转视频TextureView，使其在竖屏时播放宽屏视频
     */
    private void rotateVideoTexture() {

        videoTextureView.setRotation(90);
        float scaleX = (float) videoWidth / cameraTextureView.getWidth();
        float scaleY = (float) videoHeight / cameraTextureView.getHeight();
        videoTextureView.setScaleX(scaleX);
        videoTextureView.setScaleY(scaleY);
    }


    /**
     * 重新拍摄照片或视频
     */
    @OnClick(R.id.iv_cancel)
    void retakePictureOrVideo() {

        if (eventType == TAKE_PICTURE) {// 事件类型为拍照片

            pictureView.setImageBitmap(null);
            camera.startPreview();
        } else if (eventType == RECORD_VIDEO) {// 事件类型为录视频
            releasePlayer();

            // 添加cameraTextureView初始化摄像头
            if (cameraTextureView.getParent() == null) {
                viewFrameLayout.addView(cameraTextureView);
            }
            cameraTextureView.setVisibility(View.VISIBLE);

            videoTextureView.setVisibility(View.GONE);
        }

        eventType = 0;
        resultFile.delete();
        resultFile = null;
        showHandleButton(false);
    }

    /**
     * 发送此次拍摄的照片或视频地址到上一个Activity
     */
    @OnClick(R.id.iv_confirm)
    void sendPictureOrVideo() {
        Intent intent = new Intent();
        intent.putExtra("path", resultFile.getAbsolutePath());
        intent.putExtra("type", eventType);
        setResult(RESULT_OK, intent);
        finish();
    }

    /**
     * 初始化摄像头对象
     */
    private void initCamera() {

        try {
            camera = Camera.open();
        } catch (Exception e) {

            Toast.makeText(this, "相机无法使用", Toast.LENGTH_SHORT).show();
            return;
        }
        camera.setDisplayOrientation(90);// 旋转90度适应竖屏

        try {
            camera.setPreviewTexture(cameraTextureView.getSurfaceTexture());
        } catch (IOException e) {
            e.printStackTrace();
        }

        parameters = camera.getParameters();
        // 影响输出的图片的角度
        parameters.setRotation(90);

        chooseOptimalSize(cameraTextureView.getWidth(), cameraTextureView.getHeight());// 获取合适的尺寸

        // 自动聚焦
        List<String> focusList = parameters.getSupportedFocusModes();
        if (focusList.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        } else if (focusList.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
        } else if (focusList.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
        } else {
            parameters.setFocusMode(focusList.get(0));
        }

        camera.setParameters(parameters);
    }

    /**
     * 初始化播放器
     *
     * @param surface 视图
     */
    private void initPlayer(SurfaceTexture surface) {

        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource(resultFile.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();

            Toast.makeText(context, "录制的视频文件损坏，无法播放", Toast.LENGTH_SHORT).show();

            retakePictureOrVideo();
            return;
        }
        mediaPlayer.setSurface(new Surface(surface));
        try {
            mediaPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mediaPlayer.setLooping(true);
        mediaPlayer.start();
    }

    /**
     * 选择合适的尺寸
     * 因为默认Activity的Orientation是竖屏，所以在筛选宽高时，desireWidth和desireHeight互换了
     *
     * @param desireWidth  想要设置的宽
     * @param desireHeight 想要设置的高
     */
    private void chooseOptimalSize(int desireWidth, int desireHeight) {

        int width = 0;
        int height = 0;

        // 选择预览尺寸
        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            // 选出比宽、高稍大或相等的尺寸
            if (desireHeight <= size.width && desireWidth <= size.height) {
                width = size.width;
                height = size.height;
            }
        }
        parameters.setPreviewSize(width, height);
        System.out.println("width = " + width);
        System.out.println("height = " + height);

        // 选择照片尺寸
        for (Camera.Size size : parameters.getSupportedPictureSizes()) {
            // 选出比宽、高稍大或相等的尺寸
            if (desireHeight <= size.width && desireWidth <= size.height) {
                width = size.width;
                height = size.height;
            }
        }
        parameters.setPictureSize(width, height);
        System.out.println("width = " + width);
        System.out.println("height = " + height);

        // 选择视频尺寸
        for (Camera.Size size : parameters.getSupportedVideoSizes()) {
            // 选出比宽、高稍大或相等的尺寸
            if (desireHeight <= size.width && desireWidth <= size.height) {
                videoWidth = size.width;
                videoHeight = size.height;
            }
        }

        // 当设备录制视频支持的尺寸比屏幕的尺寸小时，选取所支持的最大尺寸
        if (videoWidth == 0 || videoHeight == 0) {
            videoWidth = parameters.getSupportedVideoSizes().get(0).width;
            videoHeight = parameters.getSupportedVideoSizes().get(0).height;
        }
        rotateVideoTexture();
        System.out.println("videoWidth = " + videoWidth);
        System.out.println("videoHeight = " + videoHeight);
    }

    /**
     * 储存照片线程
     */
    private class SavePictureThread extends Thread {
        private byte[] data;

        public SavePictureThread(byte[] data) {
            this.data = data;
        }

        @Override
        public void run() {
            String fileName = "img_" + System.currentTimeMillis() + ".jpeg";
            resultFile = new File(filePath + File.separator + fileName);
            // 储存图片
            OutputStream os = null;
            try {
                os = new FileOutputStream(resultFile);
                os.write(data);
                os.close();
            } catch (IOException e) {
                Log.w("CameraActivity", "Cannot write to " + resultFile, e);
            } finally {
                if (os != null) {// 储存照片成功
                    // 通知Handler显示照片
                    try {
                        Bitmap bitmap = getBitmap(resultFile.getAbsolutePath());
                        Message msg = new Message();
                        msg.obj = bitmap;
                        viewHandler.sendMessage(msg);
                        os.close();
                    } catch (IOException e) {
                        // Ignore
                    }
                }
            }
        }
    }

    /**
     * 拍摄照片
     */
    private void takePicture() {

        camera.takePicture(null, null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                SavePictureThread savePictureThread = new SavePictureThread(data);
                savePictureThread.start();
                camera.stopPreview();// 停止预览
            }
        });
    }

    /**
     * 显示操作按钮
     *
     * @param taken 是否已经拍摄照片/视频
     */
    private void showHandleButton(boolean taken) {
        if (taken) {
            circleProgressBar.setVisibility(View.GONE);
            hintTextView.setVisibility(View.GONE);
            confirmImageView.setVisibility(View.VISIBLE);
            cancelImageView.setVisibility(View.VISIBLE);
        } else {
            circleProgressBar.setVisibility(View.VISIBLE);
            hintTextView.setVisibility(View.VISIBLE);
            confirmImageView.setVisibility(View.GONE);
            cancelImageView.setVisibility(View.GONE);
        }
    }

    /**
     * 根据照片文件的参数进行相应旋转，使其在竖屏时能全屏显示
     *
     * @param path 图片地址
     * @return 图片经过旋转后的Bitmap
     */
    public static Bitmap getBitmap(String path) {
        Bitmap rotateBitmap = null;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            int degree;
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
                default:
                    degree = 0;
                    break;
            }

            Bitmap originBitmap = BitmapFactory.decodeFile(path);
            Matrix matrix = new Matrix();
            matrix.setRotate(degree, originBitmap.getWidth() / 2, originBitmap.getHeight() / 2);
            rotateBitmap = Bitmap.createBitmap(originBitmap, 0, 0, originBitmap.getWidth(), originBitmap.getHeight(), matrix, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return rotateBitmap;
    }

    /**
     * 在主线程处理视图操作
     */
    @SuppressLint("HandlerLeak")
    private Handler viewHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {

            pictureView.setImageBitmap((Bitmap) msg.obj);

            showHandleButton(true);

        }
    };

    /**
     * 录制视频
     */
    private void startRecord() {
        recorder = new MediaRecorder();

        camera.unlock();
        recorder.setCamera(camera);
        recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
        recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

        String fileName = "video_" + System.currentTimeMillis() + ".mp4";
        resultFile = new File(filePath + File.separator + fileName);
        recorder.setOutputFile(resultFile.getPath());
        recorder.setVideoSize(videoWidth, videoHeight);
        // 解决花屏问题
        recorder.setVideoEncodingBitRate(2000000);
        recorder.setVideoFrameRate(24);

        try {
            recorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            recorder.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放Player
     */
    private void releasePlayer() {
        if (mediaPlayer != null) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    /**
     * 释放Recorder
     */
    private void releaseRecorder() {

        if (recorder != null) {
            recorder.release();
            recorder = null;
            if (camera != null) {
                camera.lock();
            }
        }
    }

    /**
     * 释放摄像头
     */
    private void releaseCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }

    /**
     * 获取宽高比
     *
     * @param width  宽
     * @param height 高
     * @return 宽高比，如16:9
     */
    private String getAspectRatio(int width, int height) {
        int x = width;
        int y = height;
        int gcd = gcd(x, y);
        x /= gcd;
        y /= gcd;
        return x + ":" + y;
    }

    /**
     * 获取最小公约数
     *
     * @param a 第一个数字a
     * @param b 第二个数字b
     * @return a与b的最小公约数
     */
    private static int gcd(int a, int b) {
        while (b != 0) {
            int c = b;
            b = a % b;
            a = c;
        }
        return a;
    }
}
